//
// [Author] lg (https://github.com/lemos1235)
// [Date] 2023/1/18
//
import 'dart:io';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';

enum PositionShape {
  dot,
  box,
}

class PositionData {
  PositionData({
    required this.barcodes,
    required this.absoluteImageSize,
    required this.rotation,
    this.shape = PositionShape.box,
    this.barcodesVal = const [],
  });

  final List<Barcode> barcodes;
  final Size absoluteImageSize;
  final InputImageRotation rotation;
  final PositionShape shape;
  List<String> barcodesVal;

  PositionData copyWith(
      {List<Barcode>? barcodes,
      Size? absoluteImageSize,
      InputImageRotation? rotation,
      PositionShape? shape,
      List<String>? barcodesVal}) {
    return PositionData(
      barcodes: barcodes ?? this.barcodes,
      absoluteImageSize: absoluteImageSize ?? this.absoluteImageSize,
      rotation: rotation ?? this.rotation,
      shape: shape ?? this.shape,
      barcodesVal: barcodesVal ?? this.barcodesVal,
    );
  }
}

class PositionView extends StatelessWidget {
  const PositionView({
    super.key,
    required this.barcodesData,
  });

  final PositionData barcodesData;

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: PositionPainter(
        barcodes: barcodesData.barcodes,
        absoluteImageSize: barcodesData.absoluteImageSize,
        rotation: barcodesData.rotation,
        shape: barcodesData.shape,
        barcodesVal: barcodesData.barcodesVal,
      ),
    );
  }
}

class PositionPainter extends CustomPainter {
  PositionPainter({
    required this.barcodes,
    required this.absoluteImageSize,
    required this.rotation,
    required this.shape,
    required this.barcodesVal,
  });

  final List<Barcode> barcodes;
  final Size absoluteImageSize;
  final InputImageRotation rotation;
  final PositionShape shape;
  final List<String>? barcodesVal;

  double translateX(double x, InputImageRotation rotation, Size size, Size absoluteImageSize) {
    var R = size.aspectRatio / absoluteImageSize.aspectRatio;
    if (R < 1) {
      R = 1 / R;
    }
    final xP = x / (Platform.isIOS ? absoluteImageSize.width : absoluteImageSize.height);
    return (xP * R - 0.5 * R + 0.5) * size.width;
  }

  double translateY(double y, InputImageRotation rotation, Size size, Size absoluteImageSize) {
    return y * size.height / (Platform.isIOS ? absoluteImageSize.height : absoluteImageSize.width);
  }

  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..style = shape == PositionShape.dot ? PaintingStyle.fill : PaintingStyle.stroke
      ..color = Colors.white
      ..strokeWidth = 3.0;

    for (final Barcode barcode in barcodes) {
      if (barcodesVal?.contains(barcode.rawValue) ?? false) {
        paint.color = Colors.blue;
      } else {
        paint.color = Colors.white;
      }

      double left = double.infinity;
      double top = double.infinity;
      double right = double.negativeInfinity;
      double bottom = double.negativeInfinity;

      final cornerPoints = barcode.cornerPoints;
      final boundingBox = barcode.boundingBox;
      if (cornerPoints == null) {
        if (boundingBox != null) {
          left = translateX(boundingBox.left, rotation, size, absoluteImageSize);
          top = translateY(boundingBox.top, rotation, size, absoluteImageSize);
          right = translateX(boundingBox.right, rotation, size, absoluteImageSize);
          bottom = translateY(boundingBox.bottom, rotation, size, absoluteImageSize);
          if (shape == PositionShape.dot) {
            canvas.drawCircle(Offset((left + right) / 2, (top + bottom) / 2), 6, paint);
          } else {
            canvas.drawRect(Rect.fromLTRB(left, top, right, bottom), paint);
          }
        }
      } else {
        final List<Offset> offsetPoints = <Offset>[];
        for (final point in cornerPoints) {
          final double x = translateX(point.x.toDouble(), rotation, size, absoluteImageSize);
          final double y = translateY(point.y.toDouble(), rotation, size, absoluteImageSize);
          offsetPoints.add(Offset(x, y));
          // Due to possible rotations we need to find the smallest and largest
          top = min(top, y);
          bottom = max(bottom, y);
          left = min(left, x);
          right = max(right, x);
        }
        // Add the first point to close the polygon
        offsetPoints.add(offsetPoints.first);
        if (shape == PositionShape.dot) {
          canvas.drawCircle(Offset((left + right) / 2, (top + bottom) / 2), 6, paint);
        } else {
          canvas.drawPath(Path()..addPolygon(offsetPoints, true), paint);
        }
      }
    }
  }

  @override
  bool shouldRepaint(PositionPainter oldDelegate) {
    return oldDelegate.absoluteImageSize != absoluteImageSize ||
        oldDelegate.rotation != rotation ||
        oldDelegate.barcodes != barcodes ||
        oldDelegate.barcodesVal != barcodesVal;
  }
}
